import React from 'react';
import { useMemo, useRef, useEffect } from 'react';
import { convertValToStr, convertStrToVal } from '@/utils/common';
import type { ProFormInstance, SubmitterProps } from '@ant-design/pro-form';
import ProForm, {
  DrawerForm,
  ModalForm,
  ProFormText,
  ProFormDatePicker,
  ProFormDateTimePicker,
  ProFormDateRangePicker,
  ProFormDateTimeRangePicker,
  ProFormTimePicker,
  ProFormMoney,
  ProFormDigitRange,
  ProFormSelect,
  ProFormTreeSelect,
  ProFormCheckbox,
  ProFormRadio,
  ProFormCascader,
  ProFormTextArea,
} from '@ant-design/pro-form';

import { useModel } from 'umi';
import { create, patch } from '@/services/commCRUD';
import { message } from 'antd';
import SearchInputPro from './SearchInputPro';
import _ from 'lodash';
import type { FormInstance } from 'antd/es/form/Form';
// 属性类型
type PropField = {
  /** 后台dao的名称 */
  daoName?: string;
  /** 控件列表 */
  controls: API.Control[];
  /** 初始值 */
  initVals?: object;
  /** 显示方式，内部，窗口，抽屉；默认为modal */
  showType?: 'inner' | 'modal' | 'drawer';
  /** 布局方式，水平，垂直，内联；默认为vertical */
  layout?: 'horizontal' | 'vertical' | 'inline';
  /** 窗口宽度,用于在modal和drawer两种显示方式，默认为800*/
  width?: number;
  /** 是否显示，用于在modal和drawer两种显示方式时，控制显示和隐藏 */
  visible?: boolean;
  /** 提交时自动保存使用的键值对应的字段名字,不指定daoName和keyColName则不自动保存 */
  keyColName?: string;
  /** 标题，用于在modal和drawer两种显示方式时*/
  title?: string;
  /** 是否使用grid布局，如果layout:inline一般要设置为false*/
  grid?: boolean;
  /** 数据保存时使用的额外的值，比如当前用户{uid:15}*/
  extraVals?: Record<string, any>;
  /** 第一个控件是否自动获取焦点；默认为false*/
  autoFocusFirstInput?: boolean;
  /** 当控件的值发生改变时执行的回调用*/
  onValuesChange?: (changedValues: any, values: object) => void;
  /** 是否显示变时执行的回调用*/
  onVisibleChange?: (visible: boolean) => void;
  /** 表单提交时执行的回调*/
  onSumit?: (formData: object) => Promise<boolean | void>;
  /** form引用*/
  formRef?: React.MutableRefObject<ProFormInstance<any> | undefined>;
  /** 要呈现的按钮*/
  renderButtons?:
    | false
    | ((
        props: SubmitterProps<Record<string, any>> & {
          form?: FormInstance<any> | undefined;
        } & {
          submit: () => void;
          reset: () => void;
        },
        dom: JSX.Element[],
      ) => React.ReactNode | React.ReactNode[])
    | undefined;
};
const DataEdit: React.FC<PropField> = (props) => {
  const { getOptionData } = useModel('options');
  const formRef = useRef<ProFormInstance>();
  useEffect(() => {
    const retVal = formRef.current ? formRef.current?.getFieldsValue() : {};
    props.controls.forEach((c) => {
      const v = props.initVals ? props.initVals![c.name] : c.initValue;
      retVal[c.name] = convertValToStr(c, v);
    });
    if (formRef.current) {
      formRef.current?.setFieldsValue(retVal);
    }
  }, [props.controls, props.initVals]);
  useEffect(() => {
    if (!_.isEmpty(formRef?.current) && props.formRef) {
      props.formRef!.current = formRef.current;
    }
  }, [props.formRef, formRef]);
  const onFinish = async (values: { object }) => {
    props.controls.map((c) => {
      values[c.name] = convertStrToVal(c, values[c.name]);
    });
    if (props.daoName && props.keyColName) {
      let saveFunc = patch;
      values[props.keyColName] = props.initVals ? props.initVals![props.keyColName] : undefined;
      if (!values[props.keyColName]) {
        saveFunc = create;
      }
      let vals = values;
      if (!_.isEmpty(props.extraVals)) {
        vals = { ...values, ...props.extraVals };
      }
      saveFunc(props.daoName, vals)
        .then(function (data) {
          if (data) {
            if (props.onSumit) {
              props.onSumit(vals);
            }
            message.success(`${props.title}保存成功`);
          }
        })
        .catch(function (ex) {
          message.error(`${props.title}保存失败,${ex}`);
        });
    } else {
      if (props.onSumit) {
        props.onSumit(values);
      }
    }
    return true;
  };
  const proFormItems = useMemo(() => {
    const getProFormItem = (contProps: API.Control) => {
      const contType = contProps.type;
      const {
        multiple,
        fetch,
        name,
        options,
        onChange,
        disabled,
        hasAll,
        label,
        grid,
        rules,
        width,
        placeholder,
      } = contProps;
      const getDataOptions = async () => {
        let dataOptions: API.OptionItem[] | undefined;
        if (fetch && fetch.tableName) {
          const where = !fetch.where ? '' : fetch.where;
          const opts = await getOptionData(
            fetch.tableName,
            fetch.optionTpl,
            fetch.valueTpl,
            where,
            fetch.parentTpl,
            fetch.sorter,
          );
          dataOptions = opts.optionDatas;
        } else {
          dataOptions = [];
          if (!_.isEmpty(options)) {
            const d0 = options![0];
            if (d0 instanceof String) {
              (options as string[]).map((d) => dataOptions!.push({ value: d, label: d }));
            } else {
              dataOptions = options as API.OptionItem[];
            }
          }
        }
        dataOptions = dataOptions ? dataOptions : [];
        if (hasAll) {
          dataOptions = [{ value: 'all', label: '全部' }, ...dataOptions];
        }
        return dataOptions ? dataOptions : [];
      };
      let cont;
      let xl = 12;
      if (grid) {
        xl = grid! <= 1 ? grid! * 24 : grid!;
      }
      const md = 24;
      const commProps = {
        key: name,
        name,
        label,
        disabled,
        colProps: { xl, md },
        rules,
        width,
        placeholder,
        onChange: onChange,
      };
      switch (contType) {
        case 'year':
          cont = <ProFormDatePicker.Year {...commProps} />;
          break;
        case 'quarter':
          cont = <ProFormDatePicker.Quarter {...commProps} />;
          break;
        case 'month':
          cont = <ProFormDatePicker.Month {...commProps} />;
          break;
        case 'date':
          cont = <ProFormDatePicker {...commProps} />;
          break;
        case 'time':
          cont = <ProFormTimePicker {...commProps} />;
          break;
        case 'datetime':
          cont = <ProFormDateTimePicker {...commProps} />;
          break;
        case 'range-month':
          cont = <ProFormDateRangePicker {...commProps} fieldProps={{ format: 'YY-MM' }} />;
          break;
        case 'range-date':
          cont = <ProFormDateRangePicker {...commProps} />;
          break;
        case 'range-datetime':
          cont = <ProFormDateTimeRangePicker {...commProps} />;
          break;
        case 'range-number':
          cont = (
            <ProFormDigitRange
              fieldProps={{
                precision: contProps.precision,
                width: '200px',
              }}
              {...commProps}
            />
          );
          break;
        case 'select':
          cont = (
            <ProFormSelect
              {...commProps}
              request={async () => getDataOptions()}
              fieldProps={{
                showSearch: true,
                optionFilterProp: 'label',
                mode: contProps.multiple ? 'multiple' : undefined,
                onChange: onChange,
              }}
            />
          );
          break;
        case 'tree-select':
          cont = (
            <ProFormTreeSelect
              {...commProps}
              request={async () => getDataOptions()}
              fieldProps={{
                showArrow: false,
                showSearch: true,
                autoClearSearchValue: true,
                treeNodeFilterProp: 'label',
                dropdownMatchSelectWidth: false,
                multiple: multiple,
                treeCheckable: multiple,
              }}
            />
          );
          break;
        case 'cascader-select':
          cont = <ProFormCascader {...commProps} request={async () => getDataOptions()} />;
          break;
        case 'checkbox-group':
          cont = (
            <ProFormCheckbox.Group
              layout="horizontal"
              request={async () => getDataOptions()}
              {...commProps}
            />
          );
          break;
        case 'radio-group':
          cont = (
            <ProFormRadio.Group
              layout="horizontal"
              request={async () => getDataOptions()}
              {...commProps}
            />
          );
          break;
        case 'search':
          cont = (
            <SearchInputPro
              {...commProps}
              value={
                props.initVals === undefined || props.initVals![name] === undefined
                  ? ''
                  : '' + props.initVals![name]
              }
              fetch={contProps.fetch!}
              formRef={formRef}
              hasAll={hasAll}
              onSelect={contProps.fetch!.onSelect}
            />
          );
          break;
        case 'number':
          let min: number | undefined = undefined;
          let max: number | undefined = undefined;
          const idx = commProps.rules
            ? commProps.rules?.findIndex((r) => r.min !== undefined || r.max !== undefined)
            : -1;
          if (idx !== -1) {
            min = commProps.rules![idx].min;
            max = commProps.rules![idx].max;
            commProps.rules!.splice(idx);
          }
          cont = (
            <ProFormMoney
              min={min}
              max={max}
              customSymbol=""
              fieldProps={{ precision: contProps.precision }}
              {...commProps}
            />
          );
          break;
        case 'textarea':
          cont = (
            <ProFormTextArea
              {...commProps}
              fieldProps={{ showCount: true, maxLength: 200, bordered: true }}
            />
          );
          break;
        default:
          cont = <ProFormText {...commProps} />;
      }
      return cont;
    };
    return props.controls.map((c) => getProFormItem(c));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [props.controls, getOptionData]);
  const formProps = {
    formRef,
    title: props.title,
    layout: props.layout,
    width: props.width,
    grid: props.grid,
    autoFocusFirstInput: props.autoFocusFirstInput,
    onValuesChange: props.onValuesChange,
    onFinish,
    submitter: {
      render: props.renderButtons,
    },
  };
  if (props.showType === 'modal') {
    return (
      <ModalForm
        {...formProps}
        visible={props.visible}
        modalProps={{ maskClosable: false }}
        onVisibleChange={(v) => {
          if (props.onVisibleChange) {
            props.onVisibleChange(v);
          }
        }}
      >
        <ProForm.Group>{proFormItems}</ProForm.Group>
      </ModalForm>
    );
  } else if (props.showType === 'drawer') {
    return (
      <DrawerForm
        {...formProps}
        visible={props.visible}
        drawerProps={{
          maskClosable: false,
          placement: 'right',
        }}
        onVisibleChange={(v) => {
          if (props.onVisibleChange) {
            props.onVisibleChange(v);
          }
        }}
      >
        {proFormItems}
      </DrawerForm>
    );
  } else {
    return <ProForm {...formProps}>{proFormItems}</ProForm>;
  }
};
export default DataEdit;

DataEdit.defaultProps = {
  showType: 'modal',
  layout: 'vertical',
  width: 800,
  visible: false,
  title: '',
  grid: true,
  autoFocusFirstInput: false,
};
